package com.caimiao.miao.library.bluetooth;

import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothGatt;
import android.bluetooth.BluetoothProfile;
import android.content.Context;
import android.text.TextUtils;
import android.util.Log;

import com.caimiao.miao.library.bluetooth.state.IBtState;
import com.caimiao.miao.http.rxjava.transformer.SchedulerTransformer;
import com.caimiao.miao.library.utils.DecimalUtil;
import com.caimiao.miao.library.utils.tip.ToastUtil;

import java.io.UnsupportedEncodingException;
import java.lang.ref.WeakReference;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import java.util.concurrent.TimeUnit;

import io.reactivex.Observable;
import io.reactivex.ObservableEmitter;
import io.reactivex.ObservableOnSubscribe;
import io.reactivex.disposables.CompositeDisposable;
import io.reactivex.disposables.Disposable;
import io.reactivex.functions.Consumer;
import io.reactivex.functions.Function;

public class BluetoothControl {
    private BluetoothBLEInterface mBluetoothBLEInterface;
    private WeakReference<Context> mActivity;
    private ObservableEmitter<BTDevice> mFindEmitter;
    private CompositeDisposable mCompositeDisposable;

    public BluetoothControl(Context context) {
        mActivity = new WeakReference<>(context);
        mBluetoothBLEInterface = new BluetoothBLEInterface(context);
        if (mBluetoothBLEInterface.checkHasBluetooth()) {
            if (!mBluetoothBLEInterface.checkOpenBluetooth()) {
//                mBluetoothBLEInterface.requestOpenBluetooth(context);
                mBluetoothBLEInterface.enableBluetooth();
            }
        } else {
            ToastUtil.show("没有蓝牙功能");
        }
    }

    //开始自动连接TYCP的蓝牙设备
    public void startAutoConnect(String bleName) {
        if (!mBluetoothBLEInterface.checkOpenBluetooth()) {
            //如果蓝牙未开启，开启蓝牙并等待
            Disposable disposable = Observable.interval(5, 5, TimeUnit.SECONDS)
                    .map(new Function<Long, Boolean>() {
                        @Override
                        public Boolean apply(Long aLong) throws Exception {
                            return mBluetoothBLEInterface.checkOpenBluetooth();
                        }
                    })
                    .compose(SchedulerTransformer.applyIoAndUi())
                    .subscribe(new Consumer<Boolean>() {
                        @Override
                        public void accept(Boolean aBoolean) throws Exception {
                            if (aBoolean) {
                                startAutoConnect(bleName);
                                setDisposable();
                            }

                        }
                    });
            addDisposable(disposable);
            return;
        }

        //检查已匹配的蓝牙
        List<BTDevice> devices = getBondedDevices();
        int size = devices == null ? 0 : devices.size();
        BTDevice target = null;
        for (int i = 0; i < size; i++) {
            String name = devices.get(i).getName();
            if (!TextUtils.isEmpty(name) && name.startsWith(bleName)) {
                target = devices.get(i);
                break;
            }
        }
        //是否有已匹配的TYCP蓝牙设备
        if (target != null && mActivity.get() != null) {
            mBluetoothBLEInterface.connect(mActivity.get(), target.getAddress());
            return;
        }
        //监听扫描结果
        Disposable disposable = getDevices()
                .compose(SchedulerTransformer.applyIoAndUi())
                .subscribe(new Consumer<BTDevice>() {
                    @Override
                    public void accept(BTDevice btDevice) throws Exception {
                        if (btDevice != null && btDevice.getName() != null && mActivity.get() != null) {
                            if (btDevice.getName().startsWith(bleName)) {
                                mBluetoothBLEInterface.stopScan(mScanCb);
                                mBluetoothBLEInterface.connect(mActivity.get(), btDevice.getAddress());
                                setDisposable();
                            }
                        }
                    }
                });
        addDisposable(disposable);
        //开始扫描
        startDiscovery();
    }

    //蓝牙是否开启
    public boolean enableBluetooth() {
        return mBluetoothBLEInterface.isEnableBluetooth();
    }

    //获取已匹配的蓝牙设备
    public List<BTDevice> getBondedDevices() {
        return mBluetoothBLEInterface.getBondedDevices();
    }

    //监听蓝牙扫描结果
    public Observable<BTDevice> getDevices() {
        return Observable.create(new ObservableOnSubscribe<BTDevice>() {
            @Override
            public void subscribe(final ObservableEmitter<BTDevice> emitter) throws Exception {
                mFindEmitter = emitter;
            }
        });
    }

    //开始扫描蓝牙设备
    public void startDiscovery() {
        mBluetoothBLEInterface.startScan(mScanCb);
    }

    //开始连接蓝牙设备
    public void connect(BTDevice device) {
        if (mBluetoothBLEInterface.isConnect()) {
            mBluetoothBLEInterface.close();
        } else {
            mBluetoothBLEInterface.connect(mActivity.get(), device.getAddress());
        }
    }

    //重新连接
    public void reconnect() {
        if (mBluetoothBLEInterface.checkOpenBluetooth()) {
            mBluetoothBLEInterface.reconnect();
        } else {
            mBluetoothBLEInterface.enableBluetooth();
            close();
            startAutoConnect("TYCP");
        }
    }

    //关闭蓝牙连接
    public void close() {
        mBluetoothBLEInterface.close();
        setDisposable();
    }

    //蓝牙--传送数据
    public void write(byte[] value) {
        mBluetoothBLEInterface.write(value);
    }

    //蓝牙--接收数据(监听)
    public Observable<String> read(final boolean isReceHex) {
        return Observable.create(new ObservableOnSubscribe<byte[]>() {
            @Override
            public void subscribe(ObservableEmitter<byte[]> emitter) throws Exception {
                mBluetoothBLEInterface.setReadObservableEmitter(emitter);
            }
        }).map(new Function<byte[], String>() {
            @Override
            public String apply(byte[] bytes) throws Exception {

                String data;
                if (!isReceHex) { // show as char
                    data = new String(bytes);
                } else { // show as hex
                    data = DecimalUtil.bytesToHex(bytes);
                }

                return data;
            }
        });
    }

    //蓝牙--接收数据(监听)
    public Observable<byte[]> read() {
        return Observable.create(new ObservableOnSubscribe<byte[]>() {
            @Override
            public void subscribe(ObservableEmitter<byte[]> emitter) throws Exception {
                mBluetoothBLEInterface.setReadObservableEmitter(emitter);
            }
        });
    }

    //蓝牙--状态监听
    public Observable<IBtState> getBtState() {
        return Observable
                .create(new ObservableOnSubscribe<IBtState>() {
                    @Override
                    public void subscribe(ObservableEmitter<IBtState> emitter) throws Exception {
                        mBluetoothBLEInterface.setStateObservableEmitter(emitter);
                    }
                })
                .compose(SchedulerTransformer.applyThreadAndUi())
                .doOnNext(new Consumer<IBtState>() {
                    @Override
                    public void accept(IBtState iBtState) throws Exception {
                        switch (iBtState.getCode()) {
                            case IBtState.CODE_CONNECT:
                                if (iBtState.getState() != BluetoothProfile.STATE_CONNECTED) {
                                    reconnect();
                                    ToastUtil.show("蓝牙连接失败, 正在重新连接...");
                                } else {
                                    ToastUtil.show("蓝牙连接成功");
                                }
                                break;
                            case IBtState.CODE_SERVICE:
                                if (iBtState.getState() != BluetoothGatt.GATT_SUCCESS) {
                                    reconnect();
                                    ToastUtil.show("蓝牙连接失败, 正在重新连接...");
                                }
                                break;
                            case IBtState.CODE_TICKET:
                                break;
                        }
                    }
                });
    }

    private BluetoothAdapter.LeScanCallback mScanCb = new BluetoothAdapter.LeScanCallback() {
        @Override
        public void onLeScan(final BluetoothDevice device, final int rssi,
                             byte[] scanRecord) {
            final BTDevice btData = BleUtil.parseAdertisedData(scanRecord);
            String deviceName = device.getName();
            if (deviceName == null) {
                deviceName = btData.getName();
            } else {
                btData.setName(deviceName);
            }
            btData.setAddress(device.getAddress());
            btData.setState(device.getBondState());
            btData.setType(device.getType());
            if (mFindEmitter != null) mFindEmitter.onNext(btData);
            Log.e("Scan", "name =" + deviceName);
        }

    };

    private static class BleUtil {

        public static BTDevice parseAdertisedData(byte[] advertisedData) {
            List<UUID> uuids = new ArrayList<>();
            String name = null;
            if (advertisedData == null) {
                return new BTDevice(uuids, name);
            }

            ByteBuffer buffer = ByteBuffer.wrap(advertisedData).order(ByteOrder.LITTLE_ENDIAN);
            while (buffer.remaining() > 2) {
                byte length = buffer.get();
                if (length == 0) break;

                byte type = buffer.get();
                switch (type) {
                    case 0x02: // Partial list of 16-bit UUIDs
                    case 0x03: // Complete list of 16-bit UUIDs
                        while (length >= 2) {
                            uuids.add(UUID.fromString(String.format(
                                    "%08x-0000-1000-8000-00805f9b34fb", buffer.getShort())));
                            length -= 2;
                        }
                        break;
                    case 0x06: // Partial list of 128-bit UUIDs
                    case 0x07: // Complete list of 128-bit UUIDs
                        while (length >= 16) {
                            long lsb = buffer.getLong();
                            long msb = buffer.getLong();
                            uuids.add(new UUID(msb, lsb));
                            length -= 16;
                        }
                        break;
                    case 0x09:
                        byte[] nameBytes = new byte[length - 1];
                        buffer.get(nameBytes);
                        try {
                            name = new String(nameBytes, "utf-8");
                        } catch (UnsupportedEncodingException e) {
                            e.printStackTrace();
                        }
                        break;
                    default:
                        buffer.position(buffer.position() + length - 1);
                        break;
                }
            }
            return new BTDevice(uuids, name);
        }
    }

    private void addDisposable(Disposable disposable) {
        if (mCompositeDisposable == null || mCompositeDisposable.isDisposed())
            mCompositeDisposable = new CompositeDisposable();
        mCompositeDisposable.add(disposable);
    }

    private void setDisposable() {
        if (mCompositeDisposable != null)
            mCompositeDisposable.dispose();
        mCompositeDisposable = null;
    }
}
